﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Globalization;
using System.Resources;
using System.IO;
using System.Text.RegularExpressions;
using System.Xml.Linq;

using System.Threading;

namespace System.Web.Mvc
{
    public static class ResourceHtmlHelpers
    {
        const string customResourcePath="~/Resources";
        const string cookieName="mylanguage";

        public static string Resource(this HtmlHelper htmlhelper, string resourceName,string key)
        {
            return GetResource(resourceName, key);
            
        }

        public static string Resource(this Controller control, string resourceName, string key)
        {
            return GetResource(resourceName, key);
        }

        public static string Resource(string resourceName, string key)
        {
            return GetResource(resourceName, key);
        }

        private static string GetResource(string resourceName,string key)
        {
            string userLangCode = GetCulture();
            ResourceHelper internalHelper = ResourceHelper.GetInstance(HttpContext.Current.Server.MapPath(customResourcePath));
            return internalHelper.GetResource(resourceName, key, userLangCode) as string;
        }

        public static void SetLanguageCookie(string langcode)
        {
            HttpResponse Response = HttpContext.Current.Response;
            HttpCookie langcookie=new HttpCookie(cookieName);
            langcookie.Value=langcode;
            langcookie.Expires=DateTime.Now.AddMonths(1);
            Response.Cookies.Add(langcookie);
        }

        internal static string GetCulture()
        {
            HttpRequest Request = HttpContext.Current.Request;
            
            string lang = "en-us";
            
            if (Request.Cookies[cookieName] != null)
            {
                lang = Request.Cookies[cookieName].Value;
            }
            else if (Request.UserLanguages.Length > 0)
            {
                lang = Request.UserLanguages[0];
            }

            return lang;
        }

    }

    internal class ResourceHelper
    {
        const string extName = ".resx";

        internal static Dictionary<KeyValuePair<string, string>, Dictionary<string, string>> resourcesDictionary = new Dictionary<KeyValuePair<string, string>, Dictionary<string, string>>();

        private FileSystemWatcher watcher = new FileSystemWatcher();

        private static ResourceHelper instance = null;
        public static ResourceHelper GetInstance(string filePath)
        {
            if (instance == null)
            {
                instance = new ResourceHelper(filePath);
            }
            return instance;
        }

        private ResourceHelper(string filePath)
        {
            //first-time load
            ReadFromResourceFile(filePath);
            //file watcher for non-first-time load
            watcher.Path = filePath;
            watcher.IncludeSubdirectories = false;
            watcher.Filter = "*" + extName;
            watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName;
            watcher.Created += new FileSystemEventHandler(OnChanged);
            watcher.Changed += new FileSystemEventHandler(OnChanged);
            watcher.EnableRaisingEvents = true;  
        }

        private void OnChanged(object sender, FileSystemEventArgs e)
        {
            string file = e.FullPath;
            if (!File.Exists(file))
            {
                return;
            }

            if (e.ChangeType == WatcherChangeTypes.Changed || e.ChangeType == WatcherChangeTypes.Created)
            {
                //work around "file used by another program issue"
                Thread.Sleep(500);
                //ResourceReader reader = new ResourceReader(file);
                XDocument resxXML = XDocument.Load(file); 

                KeyValuePair<string, string> resourceKey = ExtractResourceNameAndLanguageCode(file);
                if (resourcesDictionary.ContainsKey(resourceKey))
                {
                    //((ResourceReader)resourcesDictionary[resourceKey]).Close();
                    resourcesDictionary.Remove(resourceKey);
                }
                //resourcesDictionary.Add(resourceKey, reader);
                resourcesDictionary.Add(resourceKey, CreateResxDictionary(resxXML));
            }
        }

        private Dictionary<string,string> CreateResxDictionary(XDocument resxRoot)
        {
            var xes=resxRoot.Root.Descendants("data")
                .Where(c => c.Attribute("name") != null && c.Element("value") != null).ToList();

            Dictionary<string, string> littleDictionary = new Dictionary<string, string>();

            foreach (var x in xes)
            {
                littleDictionary.Add(x.Attribute("name").Value, x.Element("value").Value);
            }
            return littleDictionary;
        }

        private void ReadFromResourceFile(string folderPath)
        {
            if (string.IsNullOrEmpty(folderPath))
            {
                throw new ArgumentNullException("Resource folder path should not be null or empty");
            }
            string[] files = Directory.GetFiles(folderPath);
            files = files.Where(c => c.EndsWith(extName)).ToArray();
            foreach (string file in files)
            {
                //ResourceReader reader = new ResourceReader(file);
                XDocument resxXML = XDocument.Load(file); 

                KeyValuePair<string, string> resourceKey = ExtractResourceNameAndLanguageCode(file);
                if (resourcesDictionary.ContainsKey(resourceKey))
                {
                    //((ResourceReader)resourcesDictionary[resourceKey]).Close();
                    resourcesDictionary.Remove(resourceKey);
                }
                //resourcesDictionary.Add(resourceKey, reader);
                resourcesDictionary.Add(resourceKey, CreateResxDictionary(resxXML));
            }
        }

        private KeyValuePair<string,string> ExtractResourceNameAndLanguageCode(string file)
        {
            FileInfo fi = new FileInfo(file);

            int extStartPos = fi.Name.LastIndexOf(extName);
            int langcodeStartPos = fi.Name.IndexOf('.', (extStartPos - 10) < 0 ? 0 : (extStartPos - 10));
            if (langcodeStartPos < 0)
            {
                langcodeStartPos = -1;
            }
            string langcode = fi.Name.Substring(langcodeStartPos + 1, extStartPos - langcodeStartPos - 1);

            string resourceName = fi.Name.Substring(0, langcodeStartPos == -1 ? extStartPos : langcodeStartPos);

            return new KeyValuePair<string, string>(resourceName.ToUpper(), langcode.ToUpper());
        }

        public string GetResource(string resourceName,string key,string languagecode)
        {
            KeyValuePair<string, string> resourceKey = new KeyValuePair<string, string>(resourceName.ToUpper(), languagecode.ToUpper());
            if (!ResourceHelper.resourcesDictionary.ContainsKey(resourceKey))
                return string.Empty;

            if (ResourceHelper.resourcesDictionary[resourceKey].ContainsKey(key.ToUpper()))
                return ResourceHelper.resourcesDictionary[resourceKey][key.ToUpper()];
            else
                return string.Empty;
        }

    }
}
